﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using tactics.GUI;

namespace tactics
{
    using MoveTable = Dictionary<int, UnitMoveMatrices>;

    public enum PlayState
    {
        Waiting,
        MovingUnit,
        UsingAbility,
    }

    public class TurnManager
    {
        Player currentPlayer;
        int currentTurn;
        List<Player> players;
        MoveTable moveTable;
        MovementRules moveRules;
        TerrainMap terrainMap;
        Unit unit;
        bool unitMoved;
        bool abilityUsed;

        tactics.GUI.DialogBox.okFunction okFunc;
        
        #region properties
        
        public int CurrentTurn
        {
            get
            {
                return currentTurn;
            }
        }

        public Player CurrentPlayer
        {
            get
            {
                return currentPlayer;
            }
        }

        public Unit UnitTurn
        {
            get { return unit; }
            set { unit = value; }
        }

        public bool UnitMoved
        {
            get { return unitMoved; }
            set { unitMoved = value; }
        }

        public bool AbilityUsed
        {
            get { return abilityUsed; }
            set { abilityUsed = value; }
        }

        public TerrainMap TerrainMap
        {
            get { return terrainMap; }
            set { terrainMap = value; }
        }

        public MovementRules MovementRuleSet
        {
            get { return moveRules; }
        }

        #endregion

        public TurnManager(List<Player> playersIn, MovementRules moveRulesIn, TerrainMap terrainMapIn)
        {
            moveTable = new MoveTable();

            okFunc = new GUI.DialogBox.okFunction(AllowMouse);
            players = playersIn;
            moveRules = moveRulesIn;
            terrainMap = terrainMapIn;
            currentTurn = 0;
            NewTurn();
        }

        #region Turn Handling

        public void NewTurn()
        {
            unit = null;
            unitMoved = false;
            abilityUsed = false;

            ++currentTurn;
            foreach(Player player in players)
            {
                foreach (Unit playerUnit in player.Units)
                {
                    playerUnit.Reset();
                }
            }

            // check for win after status effects process
            CheckForWin();

            NewInitiative();

            currentPlayer = GetPlayerWithInit(0);

            UpdateMoveMatrices();

            var alert = new GUI.DialogBox.DialogBox("Initative", "Rolling initiative...", currentPlayer.Name + " won the initiative!", 250, 120, false, okFunc, null);
            TacticsEngine.Instance.MouseEnabled = false;
        }

        public void EndTurn()
        {
            //Do end of turn stuff here
            NewTurn();
            UpdateTurnCard();
        }

        // check for a win, throw up the game over dialog if game is over
        public void CheckForWin()
        {
            var stillAlive = new List<Player>();

            foreach (var p in players)
                if (p.Units.Count > 0)
                    stillAlive.Add(p);

            if (stillAlive.Count == 1)
                CommandQueue.Instance.AddCommand(new Commands.ShowGameOverDialogCommand(stillAlive[0]));
            else if (stillAlive.Count == 0)
                CommandQueue.Instance.AddCommand(new Commands.ShowGameOverDialogCommand(null));
        }

        public void AdvanceTurn()
        {
            CheckForWin();

            //First advance the current initiative
            int nextInit = currentPlayer.Initiative + 1;
            
            if (nextInit == players.Count)
            {
                nextInit = 0;
            }
            //Find the player with this initiative
            Player nextPlayer = GetPlayerWithInit(nextInit);

            while (!CheckPlayerUnits(nextPlayer))
            {
                ++nextInit;
                if (nextInit == players.Count)
                {
                    nextInit = 0;
                }
                nextPlayer = GetPlayerWithInit(nextInit);

                if ((nextPlayer.Initiative == currentPlayer.Initiative) && !CheckPlayerUnits(currentPlayer))
                {
                    EndTurn();
                }
            }
            currentPlayer = nextPlayer;
            UpdateTurnCard();
            unitMoved = false;
            unit = null;
            abilityUsed = false;

            UpdatePlayerMap();
            UpdateMoveMatrices();
            TacticsEngine.Instance.Game.MiniDisplayManager.Update(players);
        }

        #endregion

        private void NewInitiative()
        {
            Random random = new Random();

            List<int> inits = new List<int>();
            for (int i = 0; i < players.Count; ++i)
            {
                inits.Add(i);
                players[i].Initiative = -1;
            }
            while (inits.Count > 0)
            {
                int rand = random.Next(inits.Count);
                players[inits.Count - 1].Initiative = inits[rand];
                inits.RemoveAt(rand);
            }
        }

        private Player GetPlayerWithInit(int init)
        {
            foreach (Player player in players)
            {
                if (init == player.Initiative)
                {
                    return player;
                }
            }
            return null;
        }

        private void UpdateTurnCard()
        {
            TacticsEngine.Instance.Game.TurnCard.Text = currentPlayer.Name;
            TacticsEngine.Instance.Game.TurnCard.Colour = currentPlayer.Colour;
        }

        private void AllowMouse()
        {
            TacticsEngine.Instance.MouseEnabled = true;
        }
        //Does the player have any units left to move
        private bool CheckPlayerUnits(Player player)
        {
            foreach (Unit unit in player.Units)
            {
                if (unit.AvailableThisTurn)
                {
                    return true;
                }
            }
            return false;
        }

        #region UnitMoveMatrix Handling

        public void UpdateMoveMatrices()
        {
            UpdatePlayerMap();
            moveTable[currentPlayer.Number] = new UnitMoveMatrices(currentPlayer, terrainMap, moveRules);
        }

        public void UpdatePlayerMap()
        {
            terrainMap.PlayerMapUpdate(players);
        }

        public Dictionary<int, int> GetUnitMoveMatrix(Unit unit)
        {
            if (unit != null && currentPlayer.Units.Contains(unit))
                return moveTable[currentPlayer.Number].GetMatrixById(unit.Id);
            else
                return null;
        }

        public void UpdateUnitMovePath(Unit unit, Utility.Pair<int, int> coord)
        {
            moveTable[currentPlayer.Number].UpdateUnitMovePath(unit, coord, terrainMap);
        }

        public Dictionary<int, int> GetUnitMovePath()
        {
            return moveTable[currentPlayer.Number].UnitsMovePath;
        }

        #endregion
    }
}